package jinyilw.swing.visuals.Rcomp;

import jinyilw.swing.i18n.IString;
import jinyilw.swing.rlaf.ui.RRootPaneUI;

import javax.swing.Icon;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JRootPane;
import javax.swing.UIManager;
import javax.swing.plaf.RootPaneUI;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.HeadlessException;
import java.awt.Window;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;

public class ROptionPane extends JOptionPane
{
	private static final long serialVersionUID = -2278617636966250975L;
	private static final ArrayList<ROptionPane> optionPanes = new ArrayList<>();
	private static final ArrayList<ROptionPane> inputPanes = new ArrayList<>();
	private Component parent;
	private RDialog dialog;
	private Window ownerWindow;
	private final ArrayList<Window> visibleWindows = new ArrayList<>();

	public ROptionPane()
	{
	}

	public ROptionPane(Object message)
	{
		this(message, PLAIN_MESSAGE);
	}

	public ROptionPane(Object message, int messageType)
	{
		this(message, messageType, DEFAULT_OPTION);
	}

	public ROptionPane(Object message, int messageType, int optionType)
	{
		this(message, messageType, optionType, null);
	}

	public ROptionPane(Object message, int messageType, int optionType,
			Icon icon)
	{
		this(message, messageType, optionType, icon, null);
	}

	public ROptionPane(Object message, int messageType, int optionType,
			Icon icon, Object[] options)
	{
		this(message, messageType, optionType, icon, options, null);
	}

	public ROptionPane(Object message, int messageType, int optionType,
			Icon icon, Object[] options, Object initialValue)
	{
		super(message, messageType, optionType, icon, options, initialValue);
	}

	public ROptionPane(Component parent, Object message, int messageType,
			int optionType, Icon icon, Object[] options, Object initialValue)
	{
		super(message, messageType, optionType, icon, options, initialValue);
		this.parent = parent;
	}

	private static ROptionPane getOptionPane(Component parent, Object message,
			int messageType, int optionType, Icon icon, Object[] options,
			Object initialValue)
	{
		for (ROptionPane pane : optionPanes)
			if (pane.parent == parent && pane.message == message
					&& pane.messageType == messageType
					&& pane.optionType == optionType && pane.options == options
					&& pane.initialValue == initialValue)
				return pane;

		ROptionPane optionPane = new ROptionPane(parent, message, messageType,
				optionType, icon, options, initialValue);
		optionPane.setInitialValue(initialValue);
		optionPane.setComponentOrientation(
				((parent == null) ? getRootFrame() : parent)
						.getComponentOrientation());
		optionPanes.add(optionPane);
		return optionPane;
	}

	private static ROptionPane getInputPane(Component parent, Object message,
			int messageType, Icon icon, Object[] selectionValues,
			Object initialSelectionValue)
	{
		for (ROptionPane pane : inputPanes)
			if (pane.parent == parent && pane.message == message
					&& pane.messageType == messageType
					&& pane.selectionValues == selectionValues
					&& pane.initialSelectionValue == initialSelectionValue)
				return pane;

		ROptionPane inputPane = new ROptionPane(parent, message, messageType,
				OK_CANCEL_OPTION, icon, null, null);
		inputPane.setWantsInput(true);
		inputPane.setSelectionValues(selectionValues);
		inputPane.setInitialSelectionValue(initialSelectionValue);
		inputPane.setComponentOrientation(
				((parent == null) ? getRootFrame() : parent)
						.getComponentOrientation());
		inputPanes.add(inputPane);
		return inputPane;
	}

	public static void showMessageDialog(Component parentComponent,
			Object message, String title) throws HeadlessException
	{
		showMessageDialog(parentComponent, message, title, INFORMATION_MESSAGE,
				null);
	}

	//	public static void showMessageDialogInThread(Component parentComponent,
	//			Object message, String title) throws HeadlessException
	//	{
	//		new Thread(new Runnable()
	//		{
	//			@Override
	//			public void run()
	//			{
	//				showMessageDialog(parentComponent, message, title,
	//						INFORMATION_MESSAGE, null);
	//			}
	//		}).start();
	//	}

	public static void showMessageDialog(Component parentComponent,
			Object message, String title, int messageType)
			throws HeadlessException
	{
		showMessageDialog(parentComponent, message, title, messageType, null);
	}

	public static void showMessageDialog(Component parentComponent,
			IString message, IString title) throws HeadlessException
	{
		showMessageDialog(parentComponent, message.getI18nString(),
				title.getI18nString(), INFORMATION_MESSAGE, null);
	}

	public static void showMessageDialog(Component parentComponent,
			IString message, IString title, int messageType)
			throws HeadlessException
	{
		showMessageDialog(parentComponent, message.getI18nString(),
				title.getI18nString(), messageType, null);
	}

	public static void showMessageDialog(Component parentComponent,
			Object message, String title, int messageType, Icon icon)
			throws HeadlessException
	{
		showOptionDialog(parentComponent, message, title, DEFAULT_OPTION,
				messageType, icon, null, null);
	}

	public static int showConfirmDialog(Component parentComponent,
			Object message, String title) throws HeadlessException
	{
		return showConfirmDialog(parentComponent, message, title, YES_NO_OPTION,
				QUESTION_MESSAGE);
	}

	public static int showConfirmDialog(Component parentComponent,
			IString message, IString title) throws HeadlessException
	{
		return showConfirmDialog(parentComponent, message.getI18nString(),
				title.getI18nString(), YES_NO_OPTION, QUESTION_MESSAGE);
	}

	public static int showConfirmDialog(Component parentComponent,
			Object message, String title, int optionType)
			throws HeadlessException
	{
		return showConfirmDialog(parentComponent, message, title, optionType,
				QUESTION_MESSAGE);
	}

	public static int showConfirmDialog(Component parentComponent,
			IString message, IString title, int optionType)
			throws HeadlessException
	{
		return showConfirmDialog(parentComponent, message.getI18nString(),
				title.getI18nString(), optionType, QUESTION_MESSAGE);
	}

	public static int showConfirmDialog(Component parentComponent,
			Object message, String title, int optionType, int messageType)
			throws HeadlessException
	{
		return showConfirmDialog(parentComponent, message, title, optionType,
				messageType, null);
	}

	public static int showConfirmDialog(Component parentComponent,
			Object message, String title, int optionType, int messageType,
			Icon icon) throws HeadlessException
	{
		return showOptionDialog(parentComponent, message, title, optionType,
				messageType, icon, null, null);
	}

	public static int showOptionDialog(Component parent, Object message,
			String title, int optionType, int messageType, Icon icon,
			Object[] options, Object initialValue) throws HeadlessException
	{
		return getOptionPane(parent, message, messageType, optionType, icon,
				options, initialValue).showOptionDialog(parent, title, null);

		//		return JOptionPane.showOptionDialog(parent, message, title, optionType,
		//				messageType, icon, options, initialValue);

	}

	public static int showOptionDialog(Component parent, Object message,
			String title, Color titleColor, int optionType, int messageType,
			Icon icon, Object[] options, Object initialValue)
			throws HeadlessException
	{
		int cc = getOptionPane(parent, message, messageType, optionType, icon,
				options, initialValue).showOptionDialog(parent, title,
				titleColor);

		return cc;

		//		return JOptionPane.showOptionDialog(parent, message, title, optionType,
		//				messageType, icon, options, initialValue);

	}

	public static String showInputDialog(Object message)
			throws HeadlessException
	{
		return showInputDialog(null, message);
	}

	public static String showInputDialog(Object message,
			Object initialSelectionValue)
	{
		return showInputDialog(null, message, initialSelectionValue);
	}

	public static String showInputDialog(Component parent, Object message)
			throws HeadlessException
	{
		return showInputDialog(parent, message, null, QUESTION_MESSAGE);
	}

	public static String showInputDialog(Component parent, Object message,
			Object initialSelectionValue)
	{
		return (String) showInputDialog(parent, message, null, QUESTION_MESSAGE,
				null, null, initialSelectionValue);
	}

	public static String showInputDialog(Component parent, Object message,
			String title, int messageType) throws HeadlessException
	{
		return (String) showInputDialog(parent, message, title, messageType,
				null, null, null);
	}

	public static Object showInputDialog(Component parent, Object message,
			String title, int messageType, Icon icon, Object[] selectionValues,
			Object initialSelectionValue) throws HeadlessException
	{
		return getInputPane(parent, message, messageType, icon, selectionValues,
				initialSelectionValue).showInputDialog(parent, title, null);
	}

	public static Object showInputDialog(Component parent, Object message,
			String title, Color titleColor, int messageType, Icon icon,
			Object[] selectionValues, Object initialSelectionValue)
			throws HeadlessException
	{
		return getInputPane(parent, message, messageType, icon, selectionValues,
				initialSelectionValue).showInputDialog(parent, title,
				titleColor);
	}

	@Override
	public JDialog createDialog(Component parent, String title)
			throws HeadlessException
	{
		setInitialValue(initialValue);
		setComponentOrientation(((parent == null) ? getRootFrame() : parent)
				.getComponentOrientation());
		Window window = getWindowForComponent(parent);
		RDialog dialog = new RDialog(window, title,
				JDialog.DEFAULT_MODALITY_TYPE);
		initDialog(dialog);
		// dialog.setAlwaysOnTop(true);
		dialog.setTitleColor(Color.yellow.brighter());
		return dialog;
	}

	private void initDialog(final JDialog dialog)
	{
		dialog.setComponentOrientation(this.getComponentOrientation());
		Container contentPane = dialog.getContentPane();
		contentPane.add(this, BorderLayout.CENTER);
		final PropertyChangeListener listener = event -> {
			if (dialog.isVisible() && event.getSource() == ROptionPane.this
					&& (event.getPropertyName().equals(VALUE_PROPERTY))
					&& event.getNewValue() != null
					&& event.getNewValue() != JOptionPane.UNINITIALIZED_VALUE)
			{
				dialog.setVisible(false);
			}
		};

		WindowAdapter adapter = new WindowAdapter()
		{
			private boolean gotFocus = false;

			@Override
			public void windowClosing(WindowEvent we)
			{
				System.out.println("windowClosing");
				setValue(null);
			}

			@Override
			public void windowClosed(WindowEvent e)
			{
				System.out.println("windowClosed");
				removePropertyChangeListener(listener);
				dialog.getContentPane().removeAll();
			}

			@Override
			public void windowGainedFocus(WindowEvent we)
			{
				if (!gotFocus)
				{
					selectInitialValue();
					gotFocus = true;
				}
			}
		};
		dialog.addWindowListener(adapter);
		dialog.addWindowFocusListener(adapter);
		dialog.addComponentListener(new ComponentAdapter()
		{
			@Override
			public void componentShown(ComponentEvent ce)
			{
				setValue(JOptionPane.UNINITIALIZED_VALUE);
			}
		});
		addPropertyChangeListener(listener);
	}

	public Object showInputDialog(Component parent, String title,
			Color titleColor)
	{
		showDialog(parent, title, titleColor);
		Object value = getInputValue();
		if (value == UNINITIALIZED_VALUE)
			return null;
		return value;
	}

	public int showOptionDialog(Component parent, String title,
			Color titleColor)
	{
		showDialog(parent, title, titleColor);
		Object selectedValue = getValue();
		if (selectedValue == null)
			return CLOSED_OPTION;
		if (options == null)
		{
			if (selectedValue instanceof Integer)
				return (int) selectedValue;
			return CLOSED_OPTION;
		}
		for (int counter = 0, maxCounter = options.length; counter < maxCounter; counter++)
		{
			if (options[counter].equals(selectedValue))
				return counter;
		}
		return CLOSED_OPTION;
	}

	private void showDialog(Component parent, String title, Color titleColor)
	{
		value = UNINITIALIZED_VALUE;
		inputValue = UNINITIALIZED_VALUE;
		dialog = getDialog(parent);
		dialog.setTitle(title);
		dialog.setTitleColor(titleColor);
		dialog.setLocationRelativeTo(parent);
		visibleWindows.clear();
		if (ownerWindow != null)
			for (Window w : ownerWindow.getOwnedWindows())
				if (w.isVisible())
				{
					visibleWindows.add(w);
					w.setVisible(false);
				}
		dialog.setVisible(true);
		dialog.dispose();
		for (Window w : visibleWindows)
			w.setVisible(true);
	}

	RDialog getDialog(Component parent) throws HeadlessException
	{
		if (dialog != null)
			return dialog;
		ownerWindow = getWindowForComponent(parent);
		dialog = new RDialog(ownerWindow, JDialog.DEFAULT_MODALITY_TYPE);
		init(dialog);
		dialog.pack();
		dialog.setResizable(false);
		selectInitialValue();
		return dialog;
	}

	static int styleFromMessageType(int messageType)
	{
		switch (messageType)
		{
			case ERROR_MESSAGE:
				return JRootPane.ERROR_DIALOG;
			case QUESTION_MESSAGE:
				return JRootPane.QUESTION_DIALOG;
			case WARNING_MESSAGE:
				return JRootPane.WARNING_DIALOG;
			case INFORMATION_MESSAGE:
				return JRootPane.INFORMATION_DIALOG;
			case PLAIN_MESSAGE:
			default:
				return JRootPane.PLAIN_DIALOG;
		}
	}

	private void init(final JDialog dialog)
	{
		dialog.setComponentOrientation(getComponentOrientation());
		Container contentPane = dialog.getContentPane();
		contentPane.add(this, BorderLayout.CENTER);
		if (JDialog.isDefaultLookAndFeelDecorated()
				&& UIManager.getLookAndFeel().getSupportsWindowDecorations())
		{
			int style = styleFromMessageType(getMessageType());
			dialog.getRootPane().setWindowDecorationStyle(style);
			if (style == JRootPane.PLAIN_DIALOG)
			{
				RootPaneUI ui = dialog.getRootPane().getUI();
				if (ui instanceof RRootPaneUI)
					((RRootPaneUI) ui).setTransBrightStyle(true);
			}
		}
		final PropertyChangeListener listener = event -> {
			if (dialog.isVisible() && event.getSource() == ROptionPane.this
					&& (event.getPropertyName().equals(VALUE_PROPERTY))
					&& event.getNewValue() != null
					&& event.getNewValue() != JOptionPane.UNINITIALIZED_VALUE)
			{
				dialog.setVisible(false);
			}
		};

		WindowAdapter adapter = new WindowAdapter()
		{
			private boolean gotFocus = false;

			@Override
			public void windowClosing(WindowEvent we)
			{
				setValue(null);
			}

			@Override
			public void windowGainedFocus(WindowEvent we)
			{
				if (!gotFocus)
				{
					selectInitialValue();
					gotFocus = true;
				}
			}
		};
		dialog.addWindowListener(adapter);
		dialog.addWindowFocusListener(adapter);
		dialog.addComponentListener(new ComponentAdapter()
		{
			@Override
			public void componentShown(ComponentEvent ce)
			{
				setValue(JOptionPane.UNINITIALIZED_VALUE);
			}
		});
		addPropertyChangeListener(listener);
	}

	static Window getWindowForComponent(Component parentComponent)
			throws HeadlessException
	{
		if (parentComponent == null)
			return getRootFrame();
		if (parentComponent instanceof Frame
				|| parentComponent instanceof Dialog)
			return (Window) parentComponent;
		return getWindowForComponent(parentComponent.getParent());
	}
}
